/*
* IDA.cpp
*
*  Created on: May 18, 2011
*      Author: federico
*/

#include "IDA.h"

IDA::IDA()
{

}

IDA::IDA(FifteenPuzzleNodeIDA So, Manhattan15Puzzle *manhattanCalculator)
{

	this->GlobalNode = So;
	this->manhattanCalculator = manhattanCalculator;
	this->heuristic = Manhattan;

}

IDA::IDA(FifteenPuzzleNodeIDA So, PDBHeuristic *fpdb, PDBHeuristic *spdb, PDBHeuristic *tpdb)
{

	this->GlobalNode = So;
	this->fpdb = fpdb;
	this->spdb = spdb;
	this->spdb = tpdb;
	this->heuristic = PDB;

}

IDA::~IDA()
{

	if(heuristic == Manhattan)
		delete manhattanCalculator;
	else
	{
		delete fpdb;
		delete spdb;
		delete tpdb;
	}

}

void IDA::Driver()
{

	int t = GlobalNode.h;

	cout << "Cotas: ";

	while(true)
	{

		cout << " " << t;

		t = DFSIDA(6,t);

		if(t == -1)
			break;
	}

	cout << " | Costo: " << moves;
}

int IDA::DFSIDA(char prev, int t)
{

	if(GlobalNode.g+GlobalNode.h > t)
		return GlobalNode.g+GlobalNode.h;


	if(GlobalNode.h == 0){
		moves = GlobalNode.g;
		return -1;
	}

	int new_t = 255, dh = GlobalNode.h, i = 1;
	short int mp[4] = {-1,-1,-1,-1};

	for(; i < 16; i++)
	{
		if(GlobalNode.invBoard[i] == GlobalNode.invBoard[0] + 4)
			mp[MOVE_UP] = i;
		else if(GlobalNode.invBoard[i] == GlobalNode.invBoard[0] - 4)
			mp[MOVE_DOWN] = i;
		else if(GlobalNode.invBoard[0] % 4 != 3 && GlobalNode.invBoard[i] == GlobalNode.invBoard[0] + 1)
			mp[MOVE_LEFT] = i;
		else if(GlobalNode.invBoard[0] % 4 != 0 && GlobalNode.invBoard[i] == GlobalNode.invBoard[0] - 1)
			mp[MOVE_RIGHT] = i;
	}

	for(i = 0; i < 4; i++)
	{
		int tp;

		switch(i){
			case MOVE_UP:
				if(GlobalNode.invBoard[0] < 12 && prev != MOVE_DOWN)
				{

					if(heuristic == Manhattan)
					{
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_UP], GlobalNode.invBoard[mp[MOVE_UP]], MOVE_UP, GlobalNode.h);
						swap(0, mp[MOVE_UP]);
					}
					else
					{
						swap(0, mp[MOVE_UP]);
						GlobalNode.h = fpdb->calculateHeuristic(GlobalNode.invBoard) + spdb->calculateHeuristic(GlobalNode.invBoard) + tpdb->calculateHeuristic(GlobalNode.invBoard);
					}

					GlobalNode.g++;

					tp = DFSIDA(MOVE_UP, t);

					if(heuristic == Manhattan)
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_UP], GlobalNode.invBoard[mp[MOVE_UP]], MOVE_DOWN, GlobalNode.h);
					else
						GlobalNode.h = dh;

					swap(0, mp[MOVE_UP]);

					GlobalNode.g--;

				}
				else continue;
				break;
			case MOVE_DOWN:
				if(GlobalNode.invBoard[0] > 3 && prev != MOVE_UP)
				{

					if(heuristic == Manhattan)
					{
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_DOWN], GlobalNode.invBoard[mp[MOVE_DOWN]], MOVE_DOWN, GlobalNode.h);
						swap(0, mp[MOVE_DOWN]);
					}
					else
					{
						swap(0, mp[MOVE_DOWN]);
						GlobalNode.h = fpdb->calculateHeuristic(GlobalNode.invBoard) + spdb->calculateHeuristic(GlobalNode.invBoard) + tpdb->calculateHeuristic(GlobalNode.invBoard);
					}

					GlobalNode.g++;

					tp = DFSIDA(MOVE_DOWN, t);

					if(heuristic == Manhattan)
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_DOWN], GlobalNode.invBoard[mp[MOVE_DOWN]], MOVE_UP, GlobalNode.h);
					else
						GlobalNode.h = dh;

					swap(0, mp[MOVE_DOWN]);

					GlobalNode.g--;

				}
				else continue;
				break;
			case MOVE_LEFT:
				if(GlobalNode.invBoard[0] % 4 != 3 && prev != MOVE_RIGHT)
				{

					if(heuristic == Manhattan)
					{
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_LEFT], GlobalNode.invBoard[mp[MOVE_LEFT]], MOVE_LEFT, GlobalNode.h);
						swap(0, mp[MOVE_LEFT]);
					}
					else
					{
						swap(0, mp[MOVE_LEFT]);
						GlobalNode.h = fpdb->calculateHeuristic(GlobalNode.invBoard) + spdb->calculateHeuristic(GlobalNode.invBoard) + tpdb->calculateHeuristic(GlobalNode.invBoard);
					}

					GlobalNode.g++;

					tp = DFSIDA(MOVE_LEFT, t);

					if(heuristic == Manhattan)
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_LEFT], GlobalNode.invBoard[mp[MOVE_LEFT]], MOVE_RIGHT, GlobalNode.h);
					else
						GlobalNode.h = dh;

					swap(0, mp[MOVE_LEFT]);

					GlobalNode.g--;

				}
				else continue;
				break;
			case MOVE_RIGHT:
				if(GlobalNode.invBoard[0] % 4 != 0 && prev != MOVE_LEFT)
				{

					if(heuristic == Manhattan)
					{
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_RIGHT], GlobalNode.invBoard[mp[MOVE_RIGHT]], MOVE_RIGHT, GlobalNode.h);
						swap(0, mp[MOVE_RIGHT]);
					}
					else
					{
						swap(0, mp[MOVE_RIGHT]);
						GlobalNode.h = fpdb->calculateHeuristic(GlobalNode.invBoard) + spdb->calculateHeuristic(GlobalNode.invBoard) + tpdb->calculateHeuristic(GlobalNode.invBoard);
					}

					GlobalNode.g++;

					tp = DFSIDA(MOVE_RIGHT, t);

					if(heuristic == Manhattan)
						GlobalNode.h = manhattanCalculator->calculateHeuristic(mp[MOVE_RIGHT], GlobalNode.invBoard[mp[MOVE_RIGHT]], MOVE_LEFT, GlobalNode.h);
					else
						GlobalNode.h = dh;

					swap(0, mp[MOVE_RIGHT]);

					GlobalNode.g--;

				}
				else continue;
				break;

		}

		if(tp == -1)
			return -1;

		new_t = MIN(new_t, tp);

	}

	return new_t;

}


void IDA::swap(int i, int j)
{
	int aux = GlobalNode.invBoard[i];
	GlobalNode.invBoard[i] = GlobalNode.invBoard[j];
	GlobalNode.invBoard[j] = aux;
}
